home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 015 / hptx501.arc / LPTX.ASM < prev    next >
Assembly Source File  |  1986-06-06  |  38KB  |  1,261 lines

  1. title LPTx : Line PrinTer Output Capture Routine
  2. page    60,132
  3. ;------------------------------------------------------------
  4. ;
  5. ;    MAIN PROGRAM    Version 5.01
  6. ;
  7. ;  (C)    Copyright 1986 by Mark DiVecchio, All Rights Reserved
  8. ;
  9. .xlist
  10. ; You may use and freely distribute this program for
  11. ; non-commercial applications.
  12. ;
  13. ; DISCLAMER : this program tries to perform a function which is
  14. ; not supported by DOS. It will work sometime and will not work
  15. ; other times. That kind of explains why you don't see this type
  16. ; of program on the market.
  17. ; I have tested it under DOS 2.1. Some users have
  18. ; reported trouble when running under DOS 3.x and for other users
  19. ; it works fine. USE AT YOUR OWN RISK.
  20. ;
  21. ; Mark C. DiVecchio
  22. ; 9067 Hillery Drive
  23. ; San Diego, CA 92126
  24. ; 619-566-6810
  25. ;------------------------------------------------------------
  26. ; Updates for Version 5.01        ?? ??? 86
  27. ;                    Still Under Development
  28. ;
  29. ; This version adds a check for DOS interuupt 21h
  30. ; function 40h for standard printer device = 0004.
  31. ;
  32. ; This addition was suggested by Dale Letterman of Seattle.
  33. ;
  34. ; Assembled using MicroSoft MASM v 4.0
  35. ;
  36. ; Program is called and used in the same way as version 3.00
  37. ;------------------------------------------------------------
  38. ; Updates for Version 5.00        13 May 86
  39. ;
  40. ; This version also takes over the DOS interuupt 21h and specifically
  41. ; checks for function 5.
  42. ; If that is the call, LPTx captures the character if LPTx has been
  43. ; activated. If it is a DOS call, LPTx assumes that DOS wants LPT1 since
  44. ; there is no way for the DOS call to specify a line printer number.
  45. ;
  46. ; Uses undocumented DOS int 21h calls 50h and 51h.
  47. ;    50h    Set new current Program Segment Prefix(PSP) from
  48. ;        segment number in BX
  49. ;    51h    Get current PSP into BX.
  50. ; These calls are used before any file is opened by the resident portion
  51. ; of LPTx. There is some concern that DOS puts information about open files
  52. ; into the current PSP. Before we open our spooler file, we want to set
  53. ; the current PSP to our PSP and then restore it after the file I/O
  54. ; is complete. This idea was expressed in PC Magazine May 13, 1986 on page
  55. ; 314 in an article by Charles Petzold.
  56. ;
  57. ;
  58. ; This version 5.0 does not obsolete versions 4.0 and 3.0. Those versions
  59. ; may work under some conditions where this one does not and vica versa.
  60. ;------------------------------------------------------------
  61. ; Updates for Version 4.01        5 May 86
  62. ;
  63. ; Had an error in the way LPTx detected if it was already in memory.
  64. ; This error existed from back in version 3.00 and may have been
  65. ; the cause of this program locking up the system the very first
  66. ; time it was called.
  67. ;------------------------------------------------------------
  68. ; Updates for Version 4.0        25 April 86
  69. ;
  70. ; Assembled using MicroSoft MASM v 4.0
  71. ;
  72. ; Program is called and used in the same way as version 3.00
  73. ;
  74. ; Modified the code to check if DOS was running when the print interrupt
  75. ; occurs. If so the print request is routed back to the regular line
  76. ; printer. This will limit the use of this capture program to user
  77. ; programs which do their own output without going to DOS.
  78. ;
  79. ; In turn, this guarantees that we do re-enter DOS.
  80. ;
  81. ; The trick of saving the DOS stack was dropped in this version and
  82. ; I have resorted to another trick which I garnered from the
  83. ; following message found on info-ibmpc. I use method number 2.
  84. ;
  85. ; This version 4.0 does not obsolete version 3.0. That version may
  86. ; work under some conditions where this one does not and vica versa.
  87. ; This one worked fine for me using DOS 2.1 and 123 version 1.A.
  88. ; Will not work with Shift PrtSc.
  89. ;
  90. comment *
  91. Date: Thu, 30 Jan 86 08:47:51 est
  92. Subject: File I/O from resident programs
  93. To: allegra!seismo!usc-isib.arpa!info-ibmpc
  94.  
  95. Regarding opening up a file when you are terminate-and-stay-resident:
  96.  
  97. Be very careful when you attempt this.  Many an FAT has been eaten for
  98. lunch when I first tried doing it.  Two ways that work like a charm:
  99.  
  100. 1)    Take over interrupt 0x28.  This interrupt gets called by DOS
  101.     while its waiting for a key to be hit. Whenever it does get
  102.     called (your program should not be time critical, btw, as
  103.     this routine is never called from CPU intensive tasks), it
  104.     is safe to do with DOS what you will. (Except for certain
  105.     interruptions, such as Search First and Search Next, which
  106.     either you'll screw-up for the foreground task, or they'll
  107.     screw-up for you.)
  108.  
  109. 2)    Get the Critical Section Flag by issuing an int 21, with ah=0x34.
  110.     This returns a pointer to a flag in ES:BX. When this flag is
  111.     NULL, and interrupts are on, it is safe to play DOS games.
  112.     Unless you are the last program to take over the interrupt,
  113.     don't trust the flag word: many "fine" programs like SideKick
  114.     do not give you a true copy of the flag word on the stack, but
  115.     rather give a simple "pushf" after interrupts are turned off.
  116. * ;end of comment
  117. ;
  118. ; More information from a message posted on USENIX:
  119. ;
  120. comment *
  121. From sdcsvax!ihnp4!timeinc!greenber Mon Jul  1 05:12:16 1985
  122. Date: 30 Jun 85 17:12:37 CDT (Sun)
  123. -----------------------------------------------------------
  124. INT 21 - Internal - Return CritSectFlag Pointer (MSDOS generic)
  125.       REG AH = 34H
  126.       On Return:
  127.             ES:BX points to DOS "Critical Section Flag"
  128.       When byte pointed to is zero, DOS is supposed to be
  129.       safe to interrupt. NOT RELIABLE according to Chris
  130.       Dunford.
  131.             Examination of DOS 2.10 code in this area
  132.       indicates that the byte immediately FOLLOWING this
  133.       "Critical Section Flag" must be 00 to permit the
  134.       PRINT.COM interrupt to be called. This suggests that
  135.       checking the WORD pointed to, rather than the BYTE,
  136.       might increase reliability of the test greatly.
  137. -----------------------------------------------------------
  138. INT 28 - Internal routine for MSDOS
  139.       This interrupt is called from inside the "get input
  140. from keyboard" routine in DOS, if and only if it is safe to use
  141. INT 21 to access the disk at that time. It is used primarily by
  142. the PRINT.COM routines, but any number of other routines could
  143. be chained to it by saving the original vector, and calling it
  144. with a FAR call (or just JMPing to it) at the end of the new
  145. routine.
  146.       Until PRINT.COM installs its own routine, this
  147. interrupt vector simply points to an IRET opcode.
  148. -----------------------------------------------------------
  149. * ;end of comment
  150. ;
  151. ;------------------------------------------------------------
  152. ; Updates for Version 3.0
  153. ;
  154. ; This version is fully compatible with IBM's PRINT command and
  155. ; hopefully most other print spoolers. I changed the method by which
  156. ; LPTx determines if a resident copy of itself is already in memory.
  157. ;
  158. ;------------------------------------------------------------
  159. ; This program intercepts the BIOS interrupt 17, the line printer
  160. ; interrupt. It will redirect the output of LPT1, LPT2, or LPT3 to a disk
  161. ; file. All three redirects may be active at the same time.
  162. ;
  163. ;
  164. ; Background:
  165. ;
  166. ; The basic problem with this type of program is that PC-DOS as written
  167. ; by Microsoft is not re-entrant. That means that if DOS is in control when
  168. ; the print interrupt occurs, you can not call DOS again to do some other
  169. ; function. Therefore, LPTx can not call DOS to write the captured print
  170. ; data to disk. Version 3.00 of LPTx tries to get around this by making
  171. ; PC-DOS re-entrant. Version 4.00 of LPTx gets around this by not ever
  172. ; trying to re-enter DOS.
  173. ;
  174. ;*******This Program Must be Converted to a .COM file before running ******
  175. ; Assemble with :
  176. ;    masm    lptx;
  177. ;    link    lptx;
  178. ;    exe2bin lptx,lptx.com
  179. ;    erase lptx.obj
  180. ;    erase lptx.exe
  181. ;
  182. ;------------------------------------------------------------
  183. ;
  184. ; Operating Instructions
  185. ;
  186. ; Calling sequence:
  187. ; lptx -1 -o <d:[pathname]filename>
  188. ;
  189. ; where -1 means redirect LPT1, -2 means redirect LPT2, -3 means redirect
  190. ;       LPT3
  191. ;       This option must appear first
  192. ;
  193. ;       -o means start the redirect to file speicfied. If rerouting
  194. ;          is already in progress for the selected line printer,
  195. ;       the old file will be closed first.
  196. ;       (If you do not specify -o but you do specify a line printer,
  197. ;       LPTx will use either the last file name that you gave when
  198. ;       you loaded LPTx or will use the file named LPTX1.LST which it
  199. ;       will create in the root directory 
  200. ;       on the default drive - where x is 1, 2, or 3.)
  201. ;
  202. ;       It is not necessary that you specify the complete path name
  203. ;       for the file. LPTx opens and closes the file each time that it
  204. ;       writes out a block. If you change directories, LPTx will 
  205. ;       be able to find the file because it save the complete path.
  206. ;
  207. ;    -c means close the file and send all furthur output directly to the
  208. ;       line printer.
  209. ;
  210. ; if neither option is specified, LPTx just displays the program status.
  211. ;
  212. ; note: -1, -2, and -3 are mutually exclusive
  213. ;       -o and -c are mutually exclusive
  214. ;
  215. ; examples:
  216. ;
  217. ; lptx                Displays the program status and loads the
  218. ;                resident portion of LPTx if it is not already
  219. ;                loaded.
  220. ;
  221. ; lptx ?            Displays a HELP screen
  222. ;
  223. ; lptx -1            routes LPT1 output to file named
  224. ;                LPTX1.LST on the default drive or the last
  225. ;                named file.
  226. ;
  227. ; lptx -o a:\able.xxx        routes LPT1 output to file named
  228. ;    or            a:\able.xxx. Any open redirection
  229. ; lptx a:\able.xxx        disk file for LPT1 is closed.
  230. ;
  231. ; lptx -2 b:xx.lst        routes LPT2 output to file named
  232. ;                XX.LST in the default directory
  233. ;                on drive B:. Any open redirection
  234. ;                disk file for LPT2 is closed.
  235. ;
  236. ; lptx -3 d:\ab\cd\file.lst    redirects LPT3 output to the file named
  237. ;                file.lst in the directory ab\cd on drive
  238. ;                d:.
  239. ;
  240. ; lptx -c            closes any disk files open for LPT1 and sends
  241. ;    or            the output back to the line printer
  242. ; lptx -1 -c            If no rerouting is taking place to LPT1,
  243. ;                this is    a NOP. LPT2 and LPT3 are not
  244. ;                affected.
  245. ;
  246. ; lptx -2 -c            closes any disk file open for LPT2 and
  247. ;                sends the output back to line printer.
  248. ;                if no rerouting is taking place to LPT2,
  249. ;                this is a NOP. LPT1 and LPT3 are not
  250. ;                affected.
  251. ;
  252. ; By rerouting LPT2 or LPT3 to a disk file, you can in effect have 2 or 3
  253. ; printers on your system. LPT1 can be your physical printer and you can
  254. ; have LPT2 output going to disk. When you redirect LPT2 or LPT3, LPT1 works
  255. ; normally.
  256. ;
  257. ; If you are rerouting to a diskette file, do not remove the diskette
  258. ; once the rerouting starts. I recommend rerouting to a hard disk or
  259. ; a RAM disk.
  260. ;
  261. ; If LPTx encounters any kind of error during the rerouting, it terminates
  262. ; operation and sends output back to the line printer. It does not display
  263. ; anything but beeps the speaker four times. This prevents your currently
  264. ; running program from possibly getting destroyed.
  265. ; An error on LPT1 redirect does not shut down LPT2 or LPT3 redirect.
  266. ;
  267. ; LPTx captures the int 17h interrupt vector. It may not operate correctly
  268. ; with other routines what also intercept that vector.
  269. ;
  270. ; Problems may occur with print spoolers which also take over the int 17h 
  271. ; vector. You can be sure that LPTx works correctly by running LPTx after
  272. ; you have run your print spooler. LPTx will be transparent to the print
  273. ; spooler but your print spooler may not be transparent to LPTx.
  274. ; LPTx works fine with IBM's PRINT command.
  275. ;
  276. ; LPTx also captures the int 24h critical error interrupt vector. This is
  277. ; done only for the period that LPTx is using the disk. This prevents
  278. ; the generation of funny error messages in the middle of other programs
  279. ; that you may be running. (LPTx justs beeps 4 times and clears itself
  280. ; out of way if a disk error occurs).
  281. ;
  282. ; LPTx can redirect all three printers to three different
  283. ; files with all 3 active at the same time.
  284. ;
  285. ; LPTx uses about 7K of memory for the resident data buffers and 
  286. ; interrupt handler.
  287. ;
  288. ; If you modify or find any bugs in this program, I would appreciate
  289. ; it if you would drop me a line with the changes. Use the address
  290. ; above.
  291. ;
  292. .list
  293. if1
  294.     %out Pass 1 - Including Macros - v5.01
  295. ;
  296. .xlist
  297. ;-----------------------------------------------------------------
  298. ;
  299. ; Macros
  300. display    macro    msg
  301.     mov    DX,offset msg
  302.     mov    AH,DISPLAY_OUTPUT
  303.     int    DOS_CALL
  304.     endm
  305. ;
  306. .list
  307. else
  308.     %out Pass 2
  309. endif
  310. ;-----------------------------------------------------------------
  311. NULL        equ    0
  312. OFF        equ    0
  313. ON        equ    1
  314. EMPTY        equ    0
  315. BEL        equ    7
  316. CR        equ    13
  317. LF        equ    10
  318. DOLLAR        equ    '$'
  319. COLON        equ    ':'
  320. BACKSLASH    equ    '\'
  321. BLANK        equ    ' '
  322. DASH        equ    '-'
  323. DOS_CALL    equ    21h
  324. BUFSIZE        equ    200h    ;size of DMA buffer
  325. DISPLAY_OUTPUT    equ    9    ;for DOS call
  326. DEF_DRIVE    equ    19h
  327. CREATE_FILE    equ    3Ch
  328. OPEN_FILE    equ    3Dh
  329. CLOSE_FILE    equ    3Eh
  330. WRITE_FILE    equ    40h
  331. DELETE_FILE    equ    41h
  332. LSEEK_FILE    equ    42h
  333. DEF_PATH    equ    47h
  334. FIND_FILE    equ    4Eh
  335. ;-----------------------------------------------------------------
  336. ;
  337. p_block    struc
  338. ;
  339. ; data structure - these variables are used only in the
  340. ; memory resident copy of LPTx. BX is set to point to the offset of the
  341. ; allocation of this structure for the selected LPT
  342. ; NOTE : all of the labels in this structure are required as place
  343. ; holders even if not referenced. Used by the initialization calls
  344. ; later on.
  345. ;
  346. active    db    OFF        ;ON = this LPTx is on, OFF = off
  347. handle    dw    NULL        ;handle of disk file used by this LPT
  348. filen    db    'a:\lptx'    ; space for redirection disk file name
  349. pnum    db    BLANK
  350.     db    '.lst',NULL
  351.     db    '                          '
  352.     db    '                        '
  353. sp_left    dw    EMPTY        ;bytes left in DMA buffer for this LPT
  354. buffer    db    BUFSIZE dup(0FFh)    ;data buffer for this LPT
  355. ;
  356. p_block    ends
  357. ;
  358. ;-----------------------------------------------------------------
  359. ;
  360. subttl    Main Code
  361. page
  362. %out Assembling CODE Segment
  363. cseg    segment para public 'CODE'
  364.     assume  CS:cseg,DS:nothing,SS:nothing
  365.     org    100h
  366. lptx:    jmp    l_start
  367. ;
  368. ; What follows is three allocations of the Structure p_block
  369. ; One for each line printer that we can support.
  370. ; With this, all three line printers have DMA buffers and flag
  371. ; variables.
  372. ; BX is used to point to the offset of the allocation currently in use
  373. ; Line printer 1
  374. lpt1        p_block    <,,,'1'>
  375. ; Line printer 2
  376. lpt2        p_block    <,,,'2'>
  377. ; Line printer 3
  378. lpt3        p_block    <,,,'3'>
  379. ;
  380. lptxe        db    bel,bel,bel,bel,DOLLAR    ;ring bell four times
  381. ;
  382. crit_flag    db    OFF    ;set to one if critical error occured
  383. off_crit    dw    0    ;save old critical error address
  384. seg_crit    dw    0
  385. ;
  386. csect_off    dw    0    ;pointer to Critical Section flag
  387. csect_seg    dw    0
  388. prt_status    db    10h    ;printer status
  389. P_NORMAL    equ    10h    ;Selected bit on only
  390. P_TIMEOUT    equ    01h    ;Time out
  391. save_psp    dw    0    ;Save area for User's PSP Segment Address
  392. lptx_psp    dw    0    ;Our PSP Segment Address
  393. byte_count    dw    0    ;to save DOS byte count
  394. stksav        dd    0    ;caller's stack EA
  395.         db    128 dup('STACK   ')
  396. stk        equ    this byte
  397. ;-----------------------------------------------------------------
  398. ;
  399. ; Interrupt handler for interrupt 17h
  400. ;
  401. int_17h    proc    far
  402. ; note : I changed this flag from version 4.00 so two versions
  403. ; of LPTx don't get intermixed.
  404.     cmp    AH,3        ;AH=3 for LPTx Function
  405.     jne    reg_call    ;This is a regular print call
  406.     jmp    ret_ack        ;This is an LPTx Call
  407. reg_call:
  408.     push    BX
  409. ; if the caller is in the critical section of DOS, LPTx is idled
  410. ; temporarily and output goes to the printer
  411.     push    DS            ; check the critical section flag
  412.     push    SI
  413.     lds    SI,dword ptr CS:csect_off
  414.     cmp    byte ptr [SI],OFF
  415.     pop    SI
  416.     pop    DS
  417.     jne    sleep17            ;DOS in critical section
  418.                     ;this indicates that we cannot
  419.                     ;do any disk operations at this
  420.                     ;time
  421. ; set up BX to point to the data area for the requested printer
  422.     cmp    DX,0        ;lpt1?
  423.     jne    chk_lpt2    ;no
  424.     mov    BX,offset lpt1    ;offset to LPT1
  425.     jmp    short bx_set
  426. chk_lpt2:
  427.     cmp    DX,1        ;lpt2?
  428.     jne    chk_lpt3    ;no
  429.     mov    BX,offset lpt2    ;offset to LPT2
  430.     jmp    short bx_set
  431. chk_lpt3:
  432.     cmp    DX,2        ;lpt3?
  433.     jne    ill_ptr        ;no - bad printer number
  434.     mov    BX,offset lpt3    ;offset to LPT3
  435. bx_set:    cmp    CS:[BX].active,OFF    ;are we active?
  436.     je    sleep17        ;no
  437.     mov    CS:prt_status,P_NORMAL    ;signal ready status
  438.     cmp    AH,1        ;initialize call?
  439.     je    do_nix        ;yes    
  440.     cmp    AH,2        ;status call?
  441.     je    do_nix        ;yes
  442.     cmp    AH,0        ;print call?
  443.     jne    do_nix        ;no
  444.     jmp    prt_17        ;we are active
  445. do_nix:    mov    AH,CS:prt_status    ;return print status
  446. rtn:    pop    BX
  447.     iret
  448. ill_ptr:mov    CS:prt_status,P_TIMEOUT    ;time out status
  449.     jmp    do_nix
  450. ret_ack:            ;return acknowledgement that I'm here
  451.     cmp    DX,0ABCh    ;my flag to detect that LPTx is
  452.                 ;already loaded and alive.
  453.     jne    ret_nak        ;return a NAK
  454.     mov    DX,0BAAAh    ;This is ack for Version 5.01
  455.     mov    AX,0AAAAh
  456.     push    CS        ;now set up ES to point to the resident
  457.     pop    ES        ; data area
  458. ret_nak:
  459.     iret            ;return to interrupted program
  460. sleep17:pop    BX        ;restore BX before we go to sleep
  461.     db    0EAh        ;jump immediate to original handler
  462. old_17h    dd    0        ;address of old int 17h routine
  463. prt_17:    push    AX        ; Start the print process.
  464.     push    BX        ; Character is in AL.
  465.     push    CX
  466.     push    DX
  467.     push    DS
  468.     push    ES
  469.     push    SI
  470.     push    DI
  471.     push    BP
  472.     push    CS        ; DS is used as the segment register
  473.     pop    DS        ; for all data during the interrupt
  474.     cli
  475.     mov    SI,SS
  476.     mov    word ptr DS:stksav+2,SI    ;save caller's stack pointer
  477.     mov    SI,SP
  478.     mov    word ptr DS:stksav,SI
  479.     mov    SI,CS
  480.     mov    SS,SI            ;give me new bigger stack
  481.     mov    SI,offset stk
  482.     mov    SP,SI
  483.     sti
  484.     mov    DS:prt_status,P_NORMAL        ;signal ready status
  485.                     ;prt_status is set before
  486.                     ;the call to prnt so that prnt
  487.                     ;can change it if the print to
  488.                     ;disk fails
  489.     call    prnt            ;print the character
  490.     cli
  491.     mov    SI,word ptr DS:stksav
  492.     mov    SP,SI            ;restore caller's stack pointer
  493.     mov    SI,word ptr DS:stksav+2
  494.     mov    SS,SI
  495.     sti
  496.     pop    BP
  497.     pop    DI
  498.     pop    SI
  499.     pop    ES
  500.     pop    DS
  501.     pop    DX
  502.     pop    CX
  503.     pop    BX
  504.     pop    AX
  505.     jmp    do_nix
  506. int_17h    endp
  507. ;-----------------------------------------------------------------
  508. ;
  509. ; Interrupt handler for interrupt 21h
  510. ;
  511. int_21h    proc    far
  512.     push    BX
  513.     cmp    AH,5            ;is this a DOS printer call?
  514.     je    int_21h_5        ;yes
  515.     cmp    AH,40h            ;is this a DOS write call?
  516.     je    int_21h_40        ;yes
  517.     jmp    sleep21            ;no - go on to real DOS
  518. ; DOS Function 40h - Write to File or Device. DS:DX contains the address
  519. ;of data to write. CX contains the byte count. Return AX = byte count.
  520. int_21h_40:                
  521.     cmp    BX,0004            ;Standard Printer?
  522.     jne    sleep21            ;no - go on to read DOS
  523. ; if the caller is in the critical section of DOS, LPTx is idled
  524. ; temporarily and output goes to the printer
  525.     push    DS            ; check the critical section flag
  526.     push    SI
  527.     lds    SI,dword ptr CS:csect_off
  528.     cmp    byte ptr [SI],OFF
  529.     pop    SI
  530.     pop    DS
  531.     jne    sleep21            ;DOS in critical section
  532.                     ;this indicates that we cannot
  533.                     ;do any disk operations at this
  534.                     ;time
  535. ; set up BX to point to the data area for the requested printer
  536. ; Since we don't know what the standard printer device is, we
  537. ; use LPT1
  538.     mov    BX,offset lpt1    ;offset to LPT1
  539.     cmp    CS:[BX].active,OFF    ;are we active?
  540.     je    sleep21            ;no
  541.     push    AX        ;now do it
  542.     push    BX
  543.     push    CX
  544.     push    DX
  545.     mov    byte_count,CX    ;save byte count
  546.     mov    BX,DX
  547. ;DS:BX points to buffer, CX has byte count
  548.     cmp    CX,0        ;check for zero byte count
  549.     je    prt_21_done
  550. loop21_40:    
  551.     mov    AL,[BX]        ;get a character
  552.     mov    AH,0        ;set up for call to interrupt 17h
  553.     int    17h        ;note : int17h returns a printer status
  554.                 ;in AH but DOS does not define a way
  555.     inc    DX
  556.     loop    loop21_40
  557. prt_21_done:
  558.     pop    DX
  559.     pop    CX
  560.     pop    BX
  561.     pop    AX
  562.     mov    AX,byte_count    ;DOS returns byte count
  563.     jmp    exit21        ;to return it to the user.
  564. ;
  565. int_21h_5:
  566. ; DOS Function 5 Printer Output. The Character in DL is output to the
  567. ; standard printer device.
  568. ; if the caller is in the critical section of DOS, LPTx is idled
  569. ; temporarily and output goes to the printer
  570.     push    DS            ; check the critical section flag
  571.     push    SI
  572.     lds    SI,dword ptr CS:csect_off
  573.     cmp    byte ptr [SI],OFF
  574.     pop    SI
  575.     pop    DS
  576.     jne    sleep21            ;DOS in critical section
  577.                     ;this indicates that we cannot
  578.                     ;do any disk operations at this
  579.                     ;time
  580. ; set up BX to point to the data area for the requested printer
  581. ; Since we don't know what the standard printer device is, we
  582. ; use LPT1
  583.     mov    BX,offset lpt1    ;offset to LPT1
  584.     cmp    CS:[BX].active,OFF    ;are we active?
  585.     je    sleep21            ;no
  586.  
  587.     push    AX
  588.     mov    AH,0        ;set up for call to interrupt 17h
  589.     mov    AL,DL        ;the character
  590.     int    17h
  591.     pop    AX
  592.                 ;note : int17h returns a printer status
  593.                 ;in AH but DOS does not define a way
  594.                 ;to return it to the user.
  595. exit21:    pop    BX
  596.     popf            ;restore flags
  597.     clc            ;never an error from us
  598.     ret            ;return to caller (regular FAR return)
  599. ;
  600. sleep21:pop    BX        ;restore BX before we go to sleep
  601.     db    0EAh        ;jump immediate to original handler
  602. old_21h    dd    0        ;address of old int 21h routine
  603. int_21h    endp
  604. ;-----------------------------------------------------------------
  605. ;
  606. ; Critical Error Handler
  607. ;
  608. crit_int    proc    far        ;got critical error
  609.     mov    CS:crit_flag,ON        ; set flag
  610.     mov    AL,0            ;tells DOS to ignore the
  611.     iret                ;error
  612. crit_int    endp
  613. ;-----------------------------------------------------------------
  614. ;
  615. ; PRNT - Print a character in AL
  616. ;
  617. prnt    proc    near
  618.     push    DS
  619.     cmp    DS:[BX].active,OFF
  620.     je    prtext            ;nothing there?
  621.     push    AX
  622.     cmp    DS:[BX].sp_left,BUFSIZE    ;buffer full
  623.     jne    intadd            ;no
  624.     call    flush            ;flush the buffer to disk
  625. ;
  626. intadd:    pop    AX
  627.     mov    DI,BX            ;offset of this printer's allocation
  628.     add    DI,offset buffer    ;add in offset of buffer
  629.     add    DI,DS:[BX].sp_left    ;add in current byte count
  630.     mov    DS:[DI],AL        ;stuff it
  631.     inc    DS:[BX].sp_left
  632. prtext:    pop    DS
  633.     ret                ;done
  634. prnt    endp
  635. ;------------------------------------------------------------
  636. ;
  637. ; FLUSH - Flush print buffer to disk file
  638. ;
  639. flush    proc    near
  640.     cmp    DS:[BX].sp_left,EMPTY    ;buffer empty?
  641.     jne    flush_buf        ;no, write it to disk
  642.     ret                ;exit
  643. flush_buf:
  644.     call    disk_out
  645.     ret
  646. flush    endp
  647. ;------------------------------------------------------------
  648. ;
  649. ; DISK_OUT - write to disk
  650. ;
  651. disk_out    proc    near
  652.     mov    DS:[BX].sp_left,EMPTY    ;set buffer empty
  653. ;PSP
  654.     push    BX
  655.     mov    AH,51h            ;get current PSP
  656.     int    DOS_CALL
  657.     mov    DS:save_psp,BX        ;and save it
  658.     mov    BX,DS:lptx_psp        ;get our PSP
  659.     mov    AH,50h
  660.     int    DOS_CALL        ;set it into DOS
  661.     pop    BX
  662. ;
  663.     push    ES
  664.     push    DS
  665.     mov    AX,DS            ;set up ES
  666.     mov    ES,AX
  667.     push    BX
  668.     push    ES
  669.     mov    AX,3524h        ;get old critical error vector
  670.     int    DOS_CALL
  671.     mov    DS:off_crit,BX
  672.     mov    DS:seg_crit,ES
  673.     mov    DX,offset crit_int
  674.     mov    AX,2524h    
  675.     int    DOS_CALL        ;trap critical error vector
  676.     mov    DS:crit_flag,OFF    ;clear critical error flag
  677.     pop    ES
  678.     pop    BX
  679.     mov    DX,BX            ;open file
  680.     add    DX,offset filen        ;filename
  681.     mov    AL,1            ;open for writing
  682.     mov    AH,open_FILE
  683.     int    DOS_CALL
  684.     mov    DS:[BX].handle,AX    ;file handle
  685.     jc    disk_err        ;error
  686.     cmp    DS:crit_flag,ON        ;critical error?
  687.     je    disk_err        ;yes
  688.     push    BX
  689.     mov    AH,lseek_FILE
  690.     mov    AL,2            ;end of file
  691.     mov    CX,0            ;offset 0
  692.     mov    DX,0
  693.     mov    BX,DS:[BX].handle
  694.     int    DOS_CALL
  695.     pop    BX
  696.     jc    disk_err        ;some seek error
  697.     cmp    DS:crit_flag,ON        ;critical error?
  698.     je    disk_err        ;yes
  699.     mov    CX,BUFSIZE        ;buffer length
  700.     mov    DX,BX            ;offset of structure allocation
  701.     add    DX,offset buffer    ;add offset of buffer within the
  702.                     ;    allocation
  703.     push    BX
  704.     mov    AH,write_FILE
  705.     mov    BX,DS:[BX].handle    ;file handle
  706.     int    DOS_CALL        ;buffer address is DS:DX
  707.     pop    BX
  708.     jnc    disk_ok
  709.     cmp    DS:crit_flag,ON        ;critical error?
  710.     je    disk_err        ;yes
  711.     cmp    AX,BUFSIZE        ;did DOS write it all?
  712.     je    disk_ok        ;yes
  713. disk_err:
  714.     display lptxe            ;ring bell
  715.     mov    DS:[BX].active,OFF    ;turn us off
  716.     mov    DS:crit_flag,OFF    ;clear error flag
  717.     mov    DS:prt_status,P_TIMEOUT    ;signal time out error
  718.                     ;then try to close the file
  719.                     ;to save what we can
  720. disk_ok:
  721.     push    BX
  722.     mov    BX,DS:[BX].handle
  723.     mov    AH,close_FILE        ;close the file
  724.     int    DOS_CALL
  725.     pop    BX
  726. disk_exit:
  727.     pop    DS
  728.     pop    ES
  729.     push    DS
  730.     lds    DX,dword ptr DS:off_crit
  731.     mov    AX,2524h        ;restore critical error vector
  732.     int    DOS_CALL
  733.     pop    DS
  734. ; PSP
  735.     push    BX
  736.     mov    BX,DS:save_psp        ;get user's PSP
  737.     mov    AH,50h
  738.     int    DOS_CALL        ;set it back into DOS
  739.     pop    BX
  740. ;
  741.     ret
  742. disk_out    endp
  743. ;
  744. end_res    db    0
  745. ;
  746. ; This is the end of the memory resident portion of LPTx
  747. ;
  748. ;--------------------------------------------------------------------
  749. ;
  750. ; All of the following data is in the Code Segment
  751. ;
  752. mach_type    db    0
  753. DOS_version    db    0        ;Major Version Number
  754.         db    0        ;Minor Version Number
  755. drive        db    0        ;default drive number 0=A etc.
  756. flag_27        db    OFF        ; 1=make this copy resident
  757. wrong_dos    db    'DOS 2.0 or later required for LPTx',LF,CR,DOLLAR
  758. up_msg        db    'LPTx - Line Printer Redirection Program - V5.01'
  759.         db    LF,CR,'   Copyright 1985 Mark C. DiVecchio',LF,CR
  760.         db    DOLLAR
  761. resident    db    LF,CR,'Resident Portion of LPTx Loaded',LF,LF,CR
  762.         db    DOLLAR
  763. lptx_err_3    db    'Could not delete file',LF,CR,DOLLAR
  764. lptx_over    db    CR,LF,'File already exists. Do you want to overwrite '
  765.         db    'it? (y or n)  :',DOLLAR
  766. lptx_nc        db    'File selection canceled',CR,LF,DOLLAR
  767. lptx_del    db    'File is being overwritten',LF,CR,DOLLAR
  768. lptx_cr        db    LF,CR,DOLLAR
  769. lptx_bad    db    'Invalid Option',LF,CR
  770.         db    'Calling sequence:',LF,CR
  771.         db    'lptx {-1,-2,-3} {-c -o <d:[pathname]filename>}'
  772.         db    LF,CR,DOLLAR
  773. lptx_on        db    LF,CR,'Redirection started. Disk file opened.'
  774.         db    LF,CR,DOLLAR
  775. lptx_off    db    LF,CR,'Redirection ended. Disk file closed.'
  776.         db    LF,CR,DOLLAR
  777. lptx_creat    db    'Could not create the disk file',LF,CR,DOLLAR
  778. ; HELP screen
  779. help_msg    db    LF,CR,'Calling sequence : ',LF,LF,CR
  780.         db    'LPTx -p -f <[d:][\pathname\pathname]filename>'
  781.         db    LF,LF,CR
  782.         db    '    where  p = printer number : 1, 2, or 3',LF,CR
  783.         db    '           f = function : o for open a print file'
  784.         db    LF,CR
  785.         db    '                          c for close a print file'
  786.         db    LF,CR
  787.         db    '           drive letter & pathname are optional'
  788.         db    LF,CR
  789.         db    '    defaults : p = 1',LF,CR
  790.         db    '               f = o',LF,CR,DOLLAR
  791. ;
  792. stat_stat    db    CR,LF,'LPTx Status :',CR,LF,DOLLAR
  793. stat_lp        db    'lpt'
  794. stat_ptr    db    ' : ',DOLLAR
  795. stat_off    db    ' not redirected',CR,LF,DOLLAR
  796. stat_dir    db    ' redirected to disk file '
  797. stat_fn        db    60 dup (BLANK)
  798. ;
  799. yn_max        db    2    ;max # of char
  800. yn_act        db    0
  801. yn_in        db    2 dup (0)
  802. ;
  803. ;--------------------------------------------------------------------
  804. ;
  805. ; This is the main routine which is executed each time that LPTx is 
  806. ; called. In this routine, DS points to the data segment which is
  807. ; transient. ES points to the data segment which is permanently
  808. ; resident. ES:BX points to the data structure for the selected 
  809. ; line printer, 1, 2, or 3.
  810. ; The offsets are the same for both. If this is the first
  811. ; time that LPTx is run, then ES=DS.
  812. ;
  813. l_start:sti        ;interrupts on
  814.     push    DS    ;Save DS
  815.     xor    AX,AX    ;clear AX for return IP
  816.     push    AX    ;put 0 on stack
  817. ;
  818. ;to check for machine type look at
  819. ; F000:FFFE                ; I don't use this currently
  820. ;    = FF    IBM PC
  821. ;    = FE    IBM XT or Portable
  822. ;    = FD    IBM PCjr
  823. ;    = FC    IBM PC AT
  824. ;    = F9    IBM Convertible
  825.     mov    AX,0F000h
  826.     mov    ES,AX
  827.     mov    BX,0FFFEh
  828.     mov    CL,ES:[BX]    ;get machine type
  829.     mov    mach_type,CL    ;save machine type
  830. ;
  831. ;PSP
  832.     mov    lptx_psp,DS    ;put away our PSP Segment address
  833. ;
  834. ; get the DOS version number
  835. ; returns zero for pre DOS 2.0 releases
  836.     mov    AH,30h
  837.     int    DOS_CALL    ;call DOS
  838.     mov    word ptr DOS_version,AX    
  839. ; Requires at least version 2.0 and may or may not work
  840. ; with versions above 2.1
  841.     cmp    DOS_version,2    ;is it DOS 2.+
  842.     jge    dos_ok        ;yes
  843.     display    wrong_dos    ;print error message
  844.     mov    AH,0
  845.     int    DOS_CALL    ;terminate
  846. dos_ok:    mov    AH,def_drive    ;get current default drive
  847.     int    DOS_CALL
  848.     mov    drive,AL    ;save the drive number
  849.     display    up_msg        ;print program ID
  850.     mov    flag_27,OFF    ;to not make resident
  851. ; are we already resident in memory?
  852.     mov    DX,0ABCh        ;check if LPTx is already resident
  853.     mov    AH,3            ;get status - special call
  854.     int    17h            ;call int 17h - BIOS
  855.     cmp    DX,0BAAAh        ;my handler sets DX to 0BAAAh
  856.                     ;and sets ES 
  857.     je    in_core            ;yes - ES has segment address
  858.     mov    flag_27,ON        ;to make this copy resident
  859.     push    CS
  860.     pop    ES            ;set ES to CS for segment address
  861.     mov    AL,drive
  862.     add    AL,'a'            ;make it a letter
  863.     mov    BX,offset lpt1
  864.     mov    ES:[BX].filen,AL    ;put it into the filename
  865.     mov    BX,offset lpt2
  866.     mov    ES:[BX].filen,AL    ;put it into the filename
  867.     mov    BX,offset lpt3
  868.     mov    ES:[BX].filen,AL    ;put it into the filename
  869. ; ----------------------------------------------------
  870. in_core:                ;ES is ok
  871. ; ES now points to resident data area
  872. ; set up ES:BX to point to default data structure
  873.     mov    BX,offset lpt1        ;offset - default to LPT1
  874. ;get options and file name
  875. ;scan input line for line printer number
  876.     mov    SI,81h            ;starting offset
  877.     mov    CL,DS:80h        ;length of input line
  878.     mov    CH,0
  879.     cmp    CX,0            ;nothing?
  880.     jne    inp_lp            ;no
  881.     jmp    make_res        ;yes, then just make LPTx resident
  882.                     ;if it isn't already
  883. inp_lp:
  884.     cmp    byte ptr DS:[SI],'?'    ;a ?  ?
  885.     jne    cont_scan        ;no
  886.     jmp    help            ;yes - go show help data
  887. cont_scan:
  888.     cmp    byte ptr DS:[SI],dash    ;a dash ?
  889.     je    got_opt            ;yes
  890.     cmp    byte ptr DS:[SI],CR    ;a carriage return?
  891.     je    scan_done        ;yes
  892.     cmp    byte ptr DS:[SI],BLANK    ;a blank?
  893.     je    inp_ret            ;yes
  894.     jmp    no_b            ;assume that we got a file name
  895.                     ;without the -o option
  896. inp_ret:
  897.     inc    SI            ;ignore blanks
  898.     loop    inp_lp            ;continue to scan
  899. ; Scan of whole line is complete, if options were not found, we
  900. ; use defaults : LPT1 and file LPTX1.LST on the default drive.
  901. ; NOTE : at least one option must be specified.
  902. scan_done:
  903.     jmp    lptx_make        ;go create the file
  904. got_opt:                ;we got an option
  905.     inc    SI            ;to option
  906.     cmp    byte ptr DS:[SI],'1'    ;LPT1?
  907.     jne    chk_2
  908.     mov    BX,offset lpt1        ;offset from ES
  909.     jmp    short inp_ret
  910. chk_2:    cmp    byte ptr DS:[SI],'2'    ;LPT2?
  911.     jne    chk_3
  912.     mov    BX,offset lpt2        ;offset from ES
  913.     jmp    short inp_ret
  914. chk_3:    cmp    byte ptr DS:[SI],'3'    ;LPT3?
  915.     jne    chk_fil
  916.     mov    BX,offset lpt3        ;offset from ES
  917.     jmp    short inp_ret
  918. chk_fil:                ;is it file?
  919.     cmp    byte ptr DS:[SI],'o'    ;open a file
  920.     je    file_op            ;yes
  921.     cmp    byte ptr DS:[SI],'c'    ;close a file
  922.     je    file_cl            ;yes
  923.     display    lptx_bad        ;incorrect option
  924.     jmp    nor_ex
  925. file_cl:                ;close the output file
  926.     cmp    ES:[BX].active,ON    ;are we active?
  927.     jne    no_close        ;no
  928.     mov    AL,1AH            ;CTRL-Z
  929.     push    DS
  930.     push    ES
  931.     pop    DS            ;set DS to point to resident
  932.                     ;data segment
  933.     call    prnt            ;print end of file mark
  934.     call    flush            ;flush out write buffer
  935.     pop    DS            ;restore DS
  936.     mov    ES:[BX].active,OFF    ;make us inactive
  937.     display lptx_off        ;redirection off message
  938. no_close:
  939.     jmp    nor_exit        ;nothing to close so exit
  940. file_op:                ;open a file for output
  941. ;get the file name
  942.     inc    SI            ;to next chracter
  943.     cmp    byte ptr DS:[SI],BLANK    ;a blank?
  944.     jne    no_b            ;no
  945.     inc    SI            ;skip over blank
  946. no_b:
  947. ; at this point, we have found a new file name. We close the old
  948. ; file if one was open
  949.     cmp    ES:[BX].active,ON    ;are we active?
  950.     jne    no_cl            ;no
  951.     mov    AL,1AH            ;CTRL-Z
  952.     push    DS
  953.     push    ES
  954.     pop    DS            ;set DS to point to resident
  955.                     ;data segment
  956.     call    prnt            ;print end of file mark
  957.     call    flush            ;flush out write buffer
  958.     pop    DS            ;restore DS
  959.     mov    ES:[BX].active,OFF    ;make us inactive
  960.     display lptx_off        ;redirection off message
  961. no_cl:    mov    DI,BX            ;base of structure
  962.     add    DI,offset filen        ;add offset of destination
  963.     push    SI            ;save pointer to file name
  964. ; search for a drive letter
  965.     inc    SI            ;should point to a colon if
  966.                     ;one is there
  967.     cmp    byte ptr [SI],COLON    ;?
  968.     je    got_drive        ;yes
  969. get_drive:
  970.     mov    AL,drive        ;get drive letter
  971.     add    AL,'a'            ;make it a letter
  972.     mov    ES:[DI],AL        ;put it in file name
  973.     inc    DI
  974.     mov    byte ptr ES:[DI],COLON    ;put in a colon
  975.     inc    DI
  976.     jmp    path_search
  977. got_drive:
  978.     pop    SI            ;move pointer back to start
  979.     mov    AL,[SI]            ;get the given drive
  980.     mov    ES:[DI],AL        ;move it
  981.     sub    AL,'a'            ;make it a number
  982.     mov    drive,AL        ;save the drive number
  983.     inc    SI
  984.     inc    DI
  985.     mov    byte ptr ES:[DI],COLON
  986.     inc    DI
  987.     inc    SI
  988.     push    SI            ;save new start pointer
  989. path_search:
  990. ; now search for a backslash which says that a pathname was given
  991. bk_s_lp:cmp    byte ptr [SI],BACKSLASH
  992.     je    got_path        ;a path
  993.     cmp    byte ptr [SI],CR    ;end of the file name?
  994.     je    get_path        ;yes with no path
  995.     inc    SI
  996.     jmp    short bk_s_lp            ;loop
  997. get_path:
  998.     mov    byte ptr ES:[DI],BACKSLASH    ;create the path
  999.     inc    DI
  1000.     mov    DL,drive        ;the current drive
  1001.     inc    DL            ;bump it for DOS
  1002.     push    DS
  1003.     push    ES
  1004.     pop    DS            ;set up DS for DOS
  1005.     mov    SI,DI            ;set up SI for pathname
  1006.     mov    AH,def_path        ;get current directory
  1007.     int    DOS_CALL        ;path goes into DS:SI
  1008.     pop    DS            ;restore DS
  1009.     cmp    byte ptr ES:[SI],NULL    ;null path?
  1010.     je    null_path        ;yes - root directory
  1011. path_lp:                ;now find the end of the string
  1012.     cmp    byte ptr ES:[SI],NULL    ;null byte marks end of pathname
  1013.     je    end_path        ;now append the file name
  1014.     inc    SI
  1015.     jmp    short path_lp
  1016. end_path:
  1017.     mov    byte ptr ES:[SI],BACKSLASH
  1018.     inc    SI
  1019. null_path:
  1020.     mov    DI,SI            ;DI is destination
  1021. got_path:
  1022.     pop    SI            ;restore source of filename
  1023. ; pick up everything to next blank
  1024. get_lp:
  1025.     mov    AL,DS:[SI]        ;character
  1026.     mov    ES:[DI],AL        ;put it away
  1027.     cmp    AL,CR            ;was it a Carriage Return?
  1028.     je    end_line
  1029.     cmp    AL,BLANK        ;was it a space?
  1030.     je    end_line
  1031.     inc    SI
  1032.     inc    DI
  1033.     jmp    short get_lp        ;no so get next character
  1034. end_line:
  1035.     mov    byte ptr ES:[DI],NULL    ;zero out the CR or blank
  1036.                     ;at the end of the filename
  1037.                     ;it becomes an ASCIIZ string
  1038.     sub    DI,BX            ;now take out the base and
  1039.     cmp    DI,offset filen        ; make sure that we got something
  1040.     jne    lptx_make        ;file name was ok
  1041.     display lptx_creat        ;could not understand the file name
  1042.     jmp    nor_exit        ;don't stay resident
  1043. nor_ex:    jmp    nor_exit
  1044.  
  1045. lptx_make:
  1046. ; default DTA used by Find File is set by DOS to an offset of
  1047. ; 80h into this program's Program Segment Prefix
  1048.     push    DS
  1049.     push    ES
  1050.     pop    DS            ;uses DS:DX
  1051.     mov    DX,BX
  1052.     add    DX,offset filen        ;file name
  1053.     mov    AH,find_FILE
  1054.     mov    CX,0            ;normal files only
  1055.     int    DOS_CALL        ;find first match
  1056.     pop    DS
  1057.     jnc    lptx_d            ;file was found
  1058.     jmp    lptx_create        ;not there - which is ok
  1059. ;file already exists
  1060. lptx_d:    display lptx_over
  1061.     mov    DX,offset yn_max;input buffer
  1062.     mov    AH,0AH
  1063.     int    DOS_CALL
  1064.     cmp    yn_act,0        ;anything typed?
  1065.     display    lptx_cr
  1066.     je    lptx_x            ;no - exit
  1067.     cmp    yn_in,'y'        ;a yes?
  1068.     je    lptx_d_yes        ;yes
  1069.     cmp    yn_in,'Y'        ;a yes?
  1070.     je    lptx_d_yes        ;yes
  1071. lptx_x:    display    lptx_nc
  1072.     jmp    nor_exit    ;all done if we can't overwrite
  1073.                 ;see if we should abort the host
  1074. lptx_d_yes:
  1075.     display lptx_del
  1076.     push    DS
  1077.     push    ES
  1078.     pop    DS            ;uses DS:DX
  1079.     mov    DX,BX
  1080.     add    DX,offset filen        ;file name
  1081.     mov    AH,delete_FILE
  1082.     int    DOS_CALL        ;delete file
  1083.     pop    DS
  1084.     jnc    lptx_create        ;ok its gone
  1085.     display lptx_err_3        ;can't delete it
  1086.     jmp    nor_exit
  1087. lptx_create:                ; create the file
  1088.     push    DS
  1089.     push    ES
  1090.     pop    DS            ;uses DS:DX
  1091.     mov    DX,BX            ;base of this LPT's structure
  1092.     add    DX,offset filen        ;file name
  1093.     mov    AH,create_FILE
  1094.     mov    CX,0            ;normal files only
  1095.     int    DOS_CALL        ;find first match
  1096.     pop    DS
  1097.     jnc    creat_ok
  1098.     display lptx_creat        ;could not create the file
  1099.     jmp    nor_exit        ;don't stay resident
  1100. creat_ok:                ;now close the file
  1101.     push    BX
  1102.     mov    BX,AX            ;AX was loaded by the create file
  1103.                     ;    call
  1104.     mov    AH,close_FILE        ;close the file
  1105.     int    DOS_CALL
  1106.     pop    BX
  1107.     display    lptx_on
  1108. ; set the program up for writing
  1109.     mov    ES:[BX].sp_left,EMPTY    ;set buffer empty
  1110.     mov    ES:[BX].active,ON    ;set us on
  1111. make_res:
  1112.     cmp    flag_27,ON        ;make this one resident?
  1113.     jne    nor_exit        ;no
  1114.     push    ES
  1115.     push    BX
  1116. ; get old interrupt handler addressses
  1117.     mov    AL,17h        ;get current vector address for 17h
  1118.     mov    AH,35h
  1119.     int    DOS_CALL
  1120.     mov    word ptr old_17h,BX
  1121.     mov    word ptr old_17h[2],ES    ;save it for later use
  1122.     mov    AL,DOS_CALL        ;get current vector address for 21h
  1123.     mov    AH,35h
  1124.     int    DOS_CALL
  1125.     mov    word ptr old_21h,BX
  1126.     mov    word ptr old_21h[2],ES    ;save it for later use
  1127. ;
  1128. ; Set LPTx up as the new int 17h interrupt handler
  1129.     mov    AX,2517h        ;set interrupt vector
  1130.     mov    DX,offset int_17h    ;BIOS printer
  1131.     int    DOS_CALL
  1132. ;
  1133. ; Set LPTx up as the new int 21h interrupt handler
  1134.     mov    AX,2521h        ;set interrupt vector
  1135.     mov    DX,offset int_21h    ;DOS Functions
  1136.     int    DOS_CALL
  1137. ;
  1138.     mov    AH,34h
  1139.     int    DOS_CALL        ;Call Special DOS interrupt
  1140.                     ;returns pointer to critical
  1141.                     ;section flag in ES:BX
  1142.                     ;With DOS 2.1, this returns
  1143.                     ;00EC:012D. I used the XRAY
  1144.                     ;program to look at this
  1145.                     ;byte while DOS was running.
  1146.     mov    csect_seg,ES        ;save the pointer
  1147.     mov    csect_off,BX
  1148.     pop    BX
  1149.     pop    ES
  1150.     display resident        ;resident loaded message
  1151.     call    stat            ;display status
  1152.     mov    DX,offset end_res
  1153.     int    27h            ;terminate but stay resident
  1154. ; HELP printer
  1155. help:    display    help_msg        ;display the HELP screen
  1156.     jmp    short nor_exit
  1157. ; Normal exit for transient copy of LPTx
  1158. nor_exit:
  1159.     call    stat            ;display status
  1160. bail_out:
  1161.     mov    AH,0
  1162.     int    DOS_CALL        ;terminate
  1163. ;------------------------------------------------------------------------
  1164. ;
  1165. ; displays the status of each of the three line printers
  1166. ;
  1167. stat    proc    near
  1168. ; display each LPTx with a message "not redirected"
  1169. ;            or redirected to <filename>
  1170.     push    AX
  1171.     push    BX
  1172.     push    DX
  1173.     push    SI
  1174.     push    DI
  1175.     display    stat_stat
  1176. stat_1:    mov    BX,offset lpt1        ;first printer
  1177.     mov    stat_ptr,'1'
  1178.     display    stat_lp
  1179.     cmp    ES:[BX].active,ON    ;are we active?
  1180.     je    stat_1_a        ;yes
  1181.     display stat_off
  1182.     jmp    short stat_2
  1183. stat_1_a:
  1184.     mov    SI,BX            ;base
  1185.     add    SI,offset filen        ;offset
  1186.     mov    DI,offset stat_fn
  1187. stat_1_lp:
  1188.     mov    AL,ES:[SI]
  1189.     mov    [DI],AL
  1190.     inc    SI
  1191.     inc    DI
  1192.     cmp    AL,NULL            ;loop till a null byte is found
  1193.     jne    stat_1_lp
  1194.     mov    byte ptr [DI],CR
  1195.     inc    DI
  1196.     mov    byte ptr [DI],LF
  1197.     inc    DI
  1198.     mov    byte ptr [DI],DOLLAR
  1199.     display stat_dir        ;display file name
  1200. stat_2:
  1201.     mov    BX,offset lpt2        ;second printer
  1202.     mov    stat_ptr,'2'
  1203.     display    stat_lp
  1204.     cmp    ES:[BX].active,ON    ;are we active?
  1205.     je    stat_2_a        ;yes
  1206.     display stat_off
  1207.     jmp    short stat_3
  1208. stat_2_a:
  1209.     mov    SI,BX            ;base
  1210.     add    SI,offset filen        ;offset
  1211.     mov    DI,offset stat_fn
  1212. stat_2_lp:
  1213.     mov    AL,ES:[SI]
  1214.     mov    [DI],AL
  1215.     inc    SI
  1216.     inc    DI
  1217.     cmp    AL,NULL            ;loop till a null byte is found
  1218.     jne    stat_2_lp
  1219.     mov    byte ptr [DI],CR
  1220.     inc    DI
  1221.     mov    byte ptr [DI],LF
  1222.     inc    DI
  1223.     mov    byte ptr [DI],DOLLAR
  1224.     display stat_dir        ;display file name
  1225. stat_3:    mov    BX,offset lpt3        ;third printer
  1226.     mov    stat_ptr,'3'
  1227.     display    stat_lp
  1228.     cmp    ES:[BX].active,ON    ;are we active?
  1229.     je    stat_3_a        ;yes
  1230.     display stat_off
  1231.     jmp    short stat_done
  1232. stat_3_a:
  1233.     mov    SI,BX            ;base
  1234.     add    SI,offset filen        ;offset
  1235.     mov    DI,offset stat_fn
  1236. stat_3_lp:
  1237.     mov    AL,ES:[SI]
  1238.     mov    [DI],AL
  1239.     inc    SI
  1240.     inc    DI
  1241.     cmp    AL,NULL            ;loop till a null byte is found
  1242.     jne    stat_3_lp
  1243.     mov    byte ptr [DI],CR
  1244.     inc    DI
  1245.     mov    byte ptr [DI],LF
  1246.     inc    DI
  1247.     mov    byte ptr [DI],DOLLAR
  1248.     display stat_dir        ;display file name
  1249. stat_done:
  1250.     pop    DI
  1251.     pop    SI
  1252.     pop    DX
  1253.     pop    BX
  1254.     pop    AX
  1255.     ret
  1256. stat    endp
  1257. ;
  1258. cseg    ends
  1259. %out EOF
  1260.     end    lptx
  1261.